﻿#ifndef ___PCLIB_S_ZIP___
#define ___PCLIB_S_ZIP___


#include "../m_core/m_core.hpp"


/*********************************************************************/
//可选功能：zlib压缩解压
/*********************************************************************/
#ifdef PCHAVE_ZLIB

#include "zip.h"
#include "unzip.h"
#if defined(PCOS_MAC) || defined(PCOS_ANDROID)
// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
#define PCFOPEN_FUNC(filename, mode) fopen(filename, mode)
#define PCFTELLO_FUNC(stream) ftell(stream)
#define PCFSEEKO_FUNC(stream, offset, origin) fseek(stream, offset, origin)
#else
#define PCFOPEN_FUNC(filename, mode) fopen64(filename, mode)
#define PCFTELLO_FUNC(stream) ftello64(stream)
#define PCFSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
#endif
#if defined(PCOS_WIN)
#define USEWIN32IOAPI
#include "iowin32.h"
#endif

/**
*@brief        zlib压缩解压功能
*/
class CPCZipHelper : CPCNoCopyable
{
public:

    /**
    *@brief     压缩一个文件或文件夹
    *@param     pszPathOrFile   [in]    文件或文件夹路径
    *@param     pszDestFilePath [in]    目标文件路径，如果目标文件存在，会被删除并覆盖
    *@param     password        [in]    密码，可以为空
    *@param     filter          [in]    文件过滤器，将文件加入压缩列表前先会经过过滤器，你可以预先拦截，返回false表示不加入压缩列表
    *@return    是否成功
    */
    static CPCResult<> PCZipCompress(const char* pszPathOrFile, const char* pszDestFilePath, const char* password = NULL,
        std::function<bool(const char* path)> filter = nullptr) 
    {
        CPCResult<> result;
        if (pszPathOrFile == NULL || pszPathOrFile[0] == 0 || pszDestFilePath == NULL || pszDestFilePath[0] == 0)
        {
            return result.SetFail("params invalid.");
        }
        if (password != NULL && strlen(password) > MAX_PATH)
        {
            return result.SetFail("password len(%d) > MAX_PATH", strlen(password));
        }
        if (password != NULL && password[0] == 0)
        {
            password = NULL;
        }

        //如果目标路径不存在，则创建
        auto result2 = CPCFileUtil::PCSeperatePath(pszDestFilePath);
        if(!result2)
        {
            return result.SetFail("PCSeperatePath fail:%s",result2.ErrDesc());
        }
        result = CPCFileUtil::PCCreateDir(result2.Get().first.c_str());
        if (!result)
        {
            return result;
        }

        //读取文件夹中的文件列表
        std::string strInitPath;
        std::vector<std::string> files;
        int type = CPCFileUtil::PCGetPathType(pszPathOrFile);
        if (type == PCFILE_TYPE_DIR)
        {
            result = CPCFileUtil::PCDeleteFile(pszDestFilePath);
            if (!result)
            {
                return result;
            }
            result = CPCFileUtil::PCScanDir(pszPathOrFile, [&files,&filter]
                (const char* pszPath, const char* pszFileName, bool bDir)->CPCResult<>
            {
                if (!bDir)
                {
                    if(!filter || filter(pszPath))
                        files.push_back(pszPath);
                }
                else
                {
                    char dirName[MAX_PATH] = { 0 };
                    strncpy(dirName, pszPath, sizeof(dirName) - 1);
                    CPCFileUtil::PCDirFormat(dirName);
                    files.push_back(dirName);
                }
                return    CPCResult<>();
            }, true);
            if (!result)
            {
                return result;
            }
            if (files.size() <= 1)
            {
                return result.SetFail("dir(%s) is empty.", pszPathOrFile);
            }
            strInitPath = pszPathOrFile;
        }
        else if (type == PCFILE_TYPE_FILE)
        {
            result = CPCFileUtil::PCDeleteFile(pszDestFilePath);
            if (!result)
            {
                return result;
            }
            if(!filter || filter(pszPathOrFile))
                files.push_back(pszPathOrFile);
            result2 = CPCFileUtil::PCSeperatePath(pszPathOrFile);
            if(!result2)
            {
                return result.SetFail("PCSeperatePath fail:%s",result2.ErrDesc());
            }
            strInitPath = result2.Get().first;
        }
        else
        {
            return result.SetFail("srcpath(%s) is not file and dir.", pszPathOrFile);
        }

        //开始压缩
        const char* initpath = strInitPath.c_str();
        size_t initpathlen = strlen(initpath);
        int opt_compress_level = Z_BEST_COMPRESSION;
        size_t nBufLen = PC_MAXSTACK;
        char* szZipBuf = new char[nBufLen];
        if (szZipBuf == NULL)
        {
            return result.SetFail("alloc(%d) fail.", nBufLen);
        }
        CPCAutoObj autoBuf([&szZipBuf](){ PCDeleteArr(szZipBuf); });

        zipFile zf;
#ifdef USEWIN32IOAPI
        zlib_filefunc64_def ffunc;
        fill_win32_filefunc64A(&ffunc);
        zf = zipOpen2_64(pszDestFilePath, 0, NULL, &ffunc);
#else
        zf = zipOpen64(pszDestFilePath, 0);
#endif
        if (zf == NULL)
        {
            return result.SetFail("zipOpen  fail.destfile:%s", pszDestFilePath);
        }
        CPCAutoObj autoZipFile([&zf](){zipClose(zf, NULL); });

        //循环压入文件/目录
        for (size_t i = 0; i<files.size(); i++)
        {
            bool bDir = (files[i][files[i].length() - 1] == '/' || files[i][files[i].length() - 1] == '\\');
            const char* filenameinzip = files[i].c_str();
            const char *savefilenameinzip;
            zip_fileinfo zi;
            unsigned long crcFile = 0;
            int zip64 = 0;

            if (!bDir)
            {
                //写入文件时间
                zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
                    zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
                zi.dosDate = 0;
                zi.internal_fa = 0;
                zi.external_fa = 0;
                PCFileTime(filenameinzip, &zi.tmz_date, &zi.dosDate);

                //写入密码
                if (password != NULL && password[0] != 0)
                {
                    result = PCGetFileCrc(filenameinzip, szZipBuf, nBufLen, &crcFile);
                    if (!result)
                    {
                        return result;
                    }
                }
                zip64 = PCIsLargeFile(filenameinzip);
            }

            /*should the zip file contain any path at all?*/
            savefilenameinzip = filenameinzip;
            if (strstr(savefilenameinzip, initpath))
            {
                savefilenameinzip = savefilenameinzip + initpathlen;
            }

            /* The path name saved, should not include a leading slash. */
            /*if it did, windows/xp and dynazip couldn't read the zip file. */
            while (savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/')
            {
                savefilenameinzip++;
            }
            if (savefilenameinzip[0] == 0)
                continue;
            std::string savefilestr = savefilenameinzip;
            CPCStrUtil::PCReplaceAll(savefilestr, "\\", "/");

            /**/
            int err = zipOpenNewFileInZip3_64(zf, savefilestr.c_str(), &zi,
                NULL, 0, NULL, 0, NULL /* comment*/,
                (opt_compress_level != 0) ? Z_DEFLATED : 0,
                opt_compress_level, 0,
                /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
                -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
                password, crcFile, zip64);
            if (err != ZIP_OK)
            {
                return result.SetFail("zip open(%s)  fail. errno:%d", filenameinzip, err);
            }
            CPCAutoObj autoCloseCurrZipFile([&zf](){zipCloseFileInZip(zf); });
            if (bDir)
                continue;

            FILE* fpZip = PCFOPEN_FUNC(filenameinzip, "rb");
            if (fpZip == NULL)
            {
                return result.SetFail("file open(%s)  fail. system errno:%d", filenameinzip, PCGetLastError());
            }
            CPCAutoObj autoCloseInputFile([&fpZip](){PCCloseFILE(fpZip); });
            while (1)
            {
                size_t nReadLen = fread(szZipBuf, 1, nBufLen, fpZip);
                if (nReadLen > nBufLen)
                {
                    return result.SetFail("fread zip buf to file (%s)  fail. system errno:%d", filenameinzip, PCGetLastError());
                }
                else if (nReadLen == 0)
                {
                    break;
                }
                err = zipWriteInFileInZip(zf, szZipBuf, nReadLen);
                if (err<0)
                {
                    return result.SetFail("zipWriteInFileInZip from file (%s)  fail. errno:%d", filenameinzip, err);
                }
            }
        }

        return result;
    }

    /**
    *@brief     解压缩一个压缩文件
    *@param     pszZipFile  [in]    压缩文件路径
    *@param     pszDestPath [in]    目标文件路径，如果目标文件存在，会被删除并覆盖
    *@param     password    [in]    密码，可以为空
    *@param     bOverWrite  [in]    如果压缩后的目标文件存在，是否覆盖它
    *@return    是否成功
    */
    static CPCResult<> PCUnZip(const char* pszZipFile, const char* pszDestPath, const char* password = NULL, bool bOverWrite = true, const char* pathmatch = NULL)
    {
        CPCResult<> result;
        if (pszZipFile == NULL || pszZipFile[0] == 0 || pszDestPath == NULL || pszDestPath[0] == 0)
        {
            return result.SetFail("params invalid.");
        }
        if (password != NULL && strlen(password) > MAX_PATH)
        {
            return result.SetFail("password len(%d) > MAX_PATH", strlen(password));
        }
        if (password != NULL && password[0] == 0)
        {
            password = NULL;
        }

        //目录不存在时，创建目录
        result = CPCFileUtil::PCCreateDir(pszDestPath);
        if (!result)
        {
            return result;
        }

        //打开压缩文件
        unzFile uf = NULL;
#ifdef USEWIN32IOAPI
        zlib_filefunc64_def ffunc;
        fill_win32_filefunc64A(&ffunc);
        uf = unzOpen2_64(pszZipFile, &ffunc);
#else
        uf = unzOpen64(pszZipFile);
#endif
        if (uf == NULL)
        {
            return result.SetFail("zipOpen fail.zipfile:%s", pszZipFile);
        }
        CPCAutoObj autoZipFile([&uf](){unzClose(uf); });

        //切换到压缩目标目录
        CPCAutoChDir autoChdir;
        result = autoChdir.ChDirTo(pszDestPath);
        if (!result)
        {
            return result;
        }

        //分配解压用的公共空间
        unsigned int nBufLen = PC_MAXSTACK;
        char* szUnZipBuf = new char[nBufLen];
        if (szUnZipBuf == NULL)
        {
            return result.SetFail("alloc(%d) fail.", nBufLen);
        }
        CPCAutoObj autoBuf([&szUnZipBuf](){ PCDeleteArr(szUnZipBuf); });

        //获取压缩信息
        unz_global_info64 gi;
        int err = unzGetGlobalInfo64(uf, &gi);
        if (err != UNZ_OK)
        {
            return result.SetFail("error %d with zipfile in unzGetGlobalInfo", err);
        }

        //遍历并解压
        int opt_overwrite = bOverWrite ? 1 : 0;
        for (ZPOS64_T i = 0; i<gi.number_entry; i++)
        {
            //获取当前文件信息
            unz_file_info64 file_info;
            char filename_inzip[MAX_PATH];
            err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip) - 1, NULL, 0, NULL, 0);

            if (pathmatch != NULL &&  pathmatch[0] != 0 && !strstr(filename_inzip, pathmatch))
            {
                //指向下一个文件
                if ((i + 1)<gi.number_entry)
                {
                    err = unzGoToNextFile(uf);
                    if (err != UNZ_OK)
                    {
                        return result.SetFail("error %d with zipfile in unzGoToNextFile", err);
                    }
                }
                continue;
            }

            if (err != UNZ_OK)
            {
                return result.SetFail("error %d with zipfile in unzGetCurrentFileInfo", err);
            }

            //获取文件名
            char* filename_withoutpath = filename_inzip;
            for (char* p = filename_withoutpath; (*p) != '\0'; p++)
            {
                if (((*p) == '/') || ((*p) == '\\'))
                {
                    filename_withoutpath = p + 1;
                }
            }

            //解压文件夹&解压文件
            if ((*filename_withoutpath) == '\0')
            {
                //创建目录
                result = CPCFileUtil::PCCreateDir(filename_inzip);
                if (!result)
                {
                    return result;
                }
            }
            else
            {
                //打开当前的
                err = unzOpenCurrentFilePassword(uf, password);
                if (err != UNZ_OK)
                {
                    return result.SetFail("error %d with zipfile in unzOpenCurrentFilePassword:%s", err, filename_inzip);
                }
                CPCAutoObj autoCloseCurrZipFile([&uf](){unzCloseCurrentFile(uf); });

                //判断是否已存在，存在就不覆盖
                if (opt_overwrite == 0 && ::access(filename_inzip, F_OK) == 0)
                {
                    //跳过
                    //printf("skip:%s\n", filename_inzip);
                }
                else
                {
                    /* some zipfile don't contain directory alone before file */
                    FILE *fout = PCFOPEN_FUNC(filename_inzip, "wb");
                    if (fout == NULL && filename_withoutpath != (char*)filename_inzip)
                    {
                        //创建目录
                        char c = *(filename_withoutpath - 1);
                        *(filename_withoutpath - 1) = '\0';
                        result = CPCFileUtil::PCCreateDir(filename_inzip);
                        if (!result)
                        {
                            return result;
                        }
                        *(filename_withoutpath - 1) = c;

                        //重新打开
                        fout = PCFOPEN_FUNC(filename_inzip, "wb");
                    }
                    if (fout == NULL)
                    {
                        return result.SetFail("error opening %s", filename_inzip);
                    }
                    CPCAutoObj autoOutFile([&fout](){PCCloseFILE(fout); });

                    //将解压后的数据写入文件
                    do
                    {
                        err = unzReadCurrentFile(uf, szUnZipBuf, nBufLen);
                        if (err<0)
                        {
                            //可能是密码错误，解压失败
                            PCCloseFILE(fout);
                            ::remove(filename_inzip);
                            return result.SetFail("error %d with zipfile in unzReadCurrentFile: %s", err, filename_inzip);
                        }
                        else if (err == 0)
                        {
                            //读取完毕
                            break;
                        }
                        else
                        {
                            if (fwrite(szUnZipBuf, err, 1, fout) != 1)
                            {
                                //写文件失败
                                err = PCGetLastError();
                                PCCloseFILE(fout);
                                ::remove(filename_inzip);
                                return result.SetFail("sys error(%d ) in writing extracted file: %s", err, filename_inzip);
                            }
                        }
                    } while (1);

                    //修改文件时间
                    PCChangeFileTime(filename_inzip, file_info.tmu_date, file_info.dosDate);
                }
            }

            //指向下一个文件
            if ((i + 1)<gi.number_entry)
            {
                err = unzGoToNextFile(uf);
                if (err != UNZ_OK)
                {
                    return result.SetFail("error %d with zipfile in unzGoToNextFile", err);
                }
            }
        }
        return result;
    }

    /**
    *@brief     将单个文件进行gzip压缩
    *@param     infile    [in]    待压缩文件路径。
    *@param     outfile   [in]    压缩后目标文件路径。
    *@return    是否成功
    */
    static CPCResult<> PCFileGZip(const char *infile, const char *outfile)
    {
        CPCResult<> result;
        if (infile == NULL || outfile == NULL ||
            infile[0] == 0 || outfile[0] == 0)
        {
            return result.SetFail("params invalid.");
        }

        gzFile out = gzopen(outfile, "wb");
        if (out == NULL)
        {
            return result.SetFail("gzopen(%s) fail.", outfile);
        }

        result = CPCFileUtil::PCScanFile(infile, [&out](const char* buf, size_t len)->CPCResult<>
        {
            gzwrite(out, buf, len);
            return CPCResult<>();
        });
        gzclose(out);
        return result;
    }

    /**
    *@brief     将单个文件进行gzip解压
    *@param     infile    [in]    待解压的压缩文件路径。
    *@param     outfile   [in]    解压后的目标文件路径。
    *@return    是否成功
    */
    static CPCResult<> PCFileGUnZip(const char *infile, const char *outfile)
    {
        CPCResult<> result;
        if (infile == NULL || outfile == NULL ||
            infile[0] == 0 || outfile[0] == 0)
        {
            return result.SetFail("params invalid.");
        }

        gzFile in = gzopen(infile, "rb");
        if (in == NULL)
        {
            return result.SetFail("gzopen(%s) fail.", infile);
        }
        CPCAutoObj autoCloseIn([&in](){gzclose(in); });

        FILE *out = ::fopen(outfile, "wb");
        if (out == NULL)
        {
            return result.SetSystemFail();
        }
        CPCAutoObj autoCloseOut([&out](){PCCloseFILE(out); });

        char buf[PC_MAXSTACK] = { 0 };
        int  len = 0;
        while ((len = gzread(in, buf, sizeof(buf))) > 0)
        {
            ::fwrite(buf, 1, len, out);
        }
        return result;
    }

    /**
    *@brief		将字节数组进行压缩，使用GZIP模式
    *@param		pszSrc		[in]	原串，未压缩的数据
    *@param		nSrcLen		[in]	原串长度
    *@param		pszDestBuf	[out]	目的串缓冲区，外部需保证长度足够
    *@param		nDestBufLen	[in]	目的串缓冲区长度
    *@return	是否成功。int代表目标串pszDestBuf转换后实际数据的长度
    */
    static CPCResult<int> PCBytesGZip(const unsigned char *pszSrc, unsigned int nSrcLen, unsigned char *pszDestBuf, unsigned int nDestBufLen)
    {
        CPCResult<int> result;
        if (pszSrc == NULL || nSrcLen == 0 || pszDestBuf == NULL || nDestBufLen == 0)
        {
            return result.SetFail("params err!  nSrcLen = %d, nDestBufLen=%d", nSrcLen, nDestBufLen);
        }
        int nRet = 0;

        z_stream c_stream;
        c_stream.zalloc = NULL;
        c_stream.zfree = NULL;
        c_stream.opaque = NULL;

        //只有设置为MAX_WBITS + 16才能在在压缩文本中带header和trailer
        nRet = deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY);
        if (nRet != Z_OK)
        {
            return result.SetFail("deflateInit2 err!  nSrcLen = %d, nDestBufLen=%d, nRet=%d", nSrcLen, nDestBufLen, nRet);
        }
        c_stream.next_in = (unsigned char *)pszSrc;
        c_stream.avail_in = nSrcLen;
        c_stream.next_out = pszDestBuf;
        c_stream.avail_out = nDestBufLen;

        while (c_stream.avail_in != 0)
        {
            if (c_stream.total_out >= nDestBufLen)
            {
                return result.SetFail("buf too short!  c_stream.total_out(%lu) >= nDestBufLen(%ld)", c_stream.total_out, nDestBufLen);
            }
            if (deflate(&c_stream, Z_NO_FLUSH) != Z_OK)
            {
                return result.SetFail("deflate err!  nSrcLen = %d, nDestBufLen=%d", nSrcLen, nDestBufLen);
            }
        }
        while (1)
        {
            nRet = deflate(&c_stream, Z_FINISH);
            if (nRet == Z_STREAM_END)
            {
                break;
            }
            if (nRet != Z_OK)
            {
                return result.SetFail("while deflate err!  nSrcLen = %d, nDestBufLen=%d, nRet=%d", nSrcLen, nDestBufLen, nRet);
            }
        }

        nRet = deflateEnd(&c_stream);
        if (nRet != Z_OK)
        {
            return result.SetFail("deflateEnd err!  nSrcLen = %d, nDestBufLen=%d, nRet=%d", nSrcLen, nDestBufLen, nRet);
        }
        return result.SetSucc(c_stream.total_out);
    }

    /**
    *@brief		将字节数组进行解压缩，使用GZIP模式
    *@param		pszSrc		[in]	原串，已经压缩好的数据
    *@param		nSrcLen		[in]	原串长度
    *@param		pszDestBuf	[out]	目的串缓冲区，外部需保证长度足够
    *@param		nDestBufLen	[in]	目的串缓冲区长度
    *@return	是否成功。int代表目标串pszDestBuf转换后实际数据的长度
    */
    static CPCResult<int>  PCBytesGUnZip(const unsigned char *pszSrc, unsigned int nSrcLen, unsigned char *pszDestBuf, unsigned int nDestBufLen)
    {
        CPCResult<int> result;
        if (pszSrc == NULL || nSrcLen == 0 || pszDestBuf == NULL || nDestBufLen == 0)
        {
            return result.SetFail("params err!  nSrcLen = %d, nDestBufLen=%d", nSrcLen, nDestBufLen);
        }
        int nRet = 0;

        static const char dummy_head[2] =
        {
            0x8 + 0x7 * 0x10,
            (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
        };

        z_stream d_stream = { 0 };
        d_stream.zalloc = NULL;
        d_stream.zfree = NULL;
        d_stream.opaque = NULL;

        d_stream.next_in = (unsigned char *)pszSrc;
        d_stream.avail_in = 0;
        d_stream.next_out = pszDestBuf;

        //只有设置为MAX_WBITS + 16才能在解压带header和trailer的文本
        //if (inflateInit2(&d_stream, MAX_WBITS + 16) != Z_OK) return -1;
        nRet = inflateInit2(&d_stream, 47);
        if (nRet != Z_OK)
        {
            return result.SetFail("inflateInit2 err!  nSrcLen = %d, nDestBufLen=%d, nRet=%d", nSrcLen, nDestBufLen, nRet);
        }

        while (d_stream.total_out < nDestBufLen && d_stream.total_in < nSrcLen)
        {
            /* force small buffers */
            d_stream.avail_in = d_stream.avail_out = 1;

            nRet = inflate(&d_stream, Z_NO_FLUSH);
            if (nRet == Z_STREAM_END)
            {
                break;
            }
            if (nRet != Z_OK)
            {
                if (nRet == Z_DATA_ERROR)
                {
                    d_stream.next_in = (unsigned char *)dummy_head;
                    d_stream.avail_in = sizeof(dummy_head);
                    nRet = inflate(&d_stream, Z_NO_FLUSH);
                    if (nRet == Z_STREAM_END)
                    {
                        break;
                    }
                    if (nRet != Z_OK)
                    {
                        return result.SetFail("Z_DATA_ERROR inflate err!  nSrcLen = %d, nDestBufLen=%d, nRet=%d", nSrcLen, nDestBufLen, nRet);
                    }
                }
                else
                {
                    return result.SetFail("inflate err!  nSrcLen = %d, nDestBufLen=%d, nRet=%d", nSrcLen, nDestBufLen, nRet);
                }
            }
        }

        nRet = inflateEnd(&d_stream);
        if (nRet != Z_OK)
        {
            return result.SetFail("inflateEnd err!  nSrcLen = %d, nDestBufLen=%d, nRet=%d", nSrcLen, nDestBufLen, nRet);
        }

        return result.SetSucc(d_stream.total_out);
    }


private:
    /**
    *@brief     取文件时间
    *@param     pszPath  [in]    文件路径。
    *@param     tmzip    [out]   tm格式时间。
    *@param     dt       [out]   uLong格式时间
    *@return    是否成功
    */
    static bool PCFileTime(const char* pszPath, tm_zip* tmzip, uLong * dt)
    {
#if defined(PCOS_WIN)
        WIN32_FIND_DATAA ff32;
        HANDLE hFind = FindFirstFileA(pszPath, &ff32);
        if (hFind == INVALID_HANDLE_VALUE)
        {
            return false;
        }
        FILETIME ftLocal;
        FileTimeToLocalFileTime(&(ff32.ftLastWriteTime), &ftLocal);
        FileTimeToDosDateTime(&ftLocal, ((LPWORD)dt) + 1, ((LPWORD)dt) + 0);
        FindClose(hFind);
        return true;
#else
        struct stat s;        /* results of stat() */
        struct tm* filedate;
        time_t tm_t = 0;
        bool bret = false;
        if (strcmp(pszPath, "-") != 0)
        {
            char name[MAX_PATH + 1];
            int len = strlen(pszPath);
            if (len > MAX_PATH)
                len = MAX_PATH;

            strncpy(name, pszPath, MAX_PATH - 1);
            /* strncpy doesnt append the trailing NULL, of the string is too long. */
            name[MAX_PATH] = '\0';

            if (name[len - 1] == '/')
                name[len - 1] = '\0';
            /* not all systems allow stat'ing a file with / appended */
            if (stat(name, &s) == 0)
            {
                tm_t = s.st_mtime;
                bret = true;
            }
        }
        filedate = localtime(&tm_t);

        tmzip->tm_sec = filedate->tm_sec;
        tmzip->tm_min = filedate->tm_min;
        tmzip->tm_hour = filedate->tm_hour;
        tmzip->tm_mday = filedate->tm_mday;
        tmzip->tm_mon = filedate->tm_mon;
        tmzip->tm_year = filedate->tm_year;
        return bret;
#endif
    }

    /**
    *@brief     检查一个文件是不是大文件
    *@param     filename    [in]    输入的包括文件名的全路径。路径分隔符支持\和/
    *@return    是否为大文件。1是0否。如果函数中间环境失败，默认返回0
    */
    static int PCIsLargeFile(const char* filename)
    {
        int largeFile = 0;
        ZPOS64_T pos = 0;
        FILE* pFile = PCFOPEN_FUNC(filename, "rb");
        if (pFile != NULL)
        {
            int n = PCFSEEKO_FUNC(pFile, 0, SEEK_END);
            pos = PCFTELLO_FUNC(pFile);
            if (pos >= 0xffffffff) largeFile = 1;
            fclose(pFile);
        }
        return largeFile;
    }

    /**
    *@brief     获取文件CRC32值，利用已有buf
    *@param     filenameinzip   [in]    文件路径
    *@param     buf             [in]    buf
    *@param     size_buf        [in]    buf长度
    *@param     result_crc      [out]    输出crc
    *@return    是否成功。
    */
    static CPCResult<>  PCGetFileCrc(const char* filenameinzip, void*buf, unsigned long size_buf, unsigned long* result_crc)
    {
        CPCResult<> result;

        FILE* fp = PCFOPEN_FUNC(filenameinzip, "rb");
        if (fp == NULL)
        {
            return result.SetSystemFail();
        }
        CPCAutoObj autoBuf([&fp](){if (fp) fclose(fp); });

        unsigned long calculate_crc = 0;
        while (1)
        {
            size_t nReadLen = fread(buf, 1, size_buf, fp);
            if (nReadLen > size_buf)
            {
                return result.SetSystemFail();
            }
            else if (nReadLen == 0)
            {
                break;
            }
            calculate_crc = crc32(calculate_crc, (const Bytef *)buf, nReadLen);
        }
        *result_crc = calculate_crc;
        return result;
    }

    /**
    *@brief     修改文件时间
    *@param     filename    [in]    文件路径。
    *@param     tmu_date    [in]    tm格式时间。 the SAME new date at the tm_unz format
    *@param     dosdate     [in]    uLong格式时间。4 bytes
    *@return    是否成功
    */
    static bool PCChangeFileTime(const char * filename, tm_unz tmu_date, uLong dosdate)
    {
#if defined(PCOS_WIN)
        FILETIME ftm, ftLocal, ftCreate, ftLastAcc, ftLastWrite;

        HANDLE hFile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
            0, NULL, OPEN_EXISTING, 0, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            return false;
        }
        GetFileTime(hFile, &ftCreate, &ftLastAcc, &ftLastWrite);
        DosDateTimeToFileTime((unsigned short)(dosdate >> 16), (unsigned short)dosdate, &ftLocal);
        LocalFileTimeToFileTime(&ftLocal, &ftm);
        SetFileTime(hFile, &ftm, &ftLastAcc, &ftm);
        PCCloseHandle(hFile);
#else
        struct utimbuf ut;
        struct tm newdate;
        newdate.tm_sec = tmu_date.tm_sec;
        newdate.tm_min = tmu_date.tm_min;
        newdate.tm_hour = tmu_date.tm_hour;
        newdate.tm_mday = tmu_date.tm_mday;
        newdate.tm_mon = tmu_date.tm_mon;
        if (tmu_date.tm_year > 1900)
            newdate.tm_year = tmu_date.tm_year - 1900;
        else
            newdate.tm_year = tmu_date.tm_year;
        newdate.tm_isdst = -1;

        ut.actime = ut.modtime = mktime(&newdate);
        utime(filename, &ut);
#endif
        return true;
    }
};
#endif    //PCHAVE_ZLIB    


#endif    //___PCLIB_S_ZIP___
